﻿/**
* Project: jObjective: Javascript Object Library for AJAX
* File name: jObjective.*
* Description:  jObjective aims to create a custom object orientated layer from HTML Javascript DOM elements.
* The layer simplifies access to data contained in various elements, allowing for single get and set methods to generate JSON objects of * complex form layouts particularly, but not exclusively, for transmission via AJAX.
*   
* Dustan Jones, Copyright (C) 2011.
*   
* The GNU Public License (GPL)
*/
/* 
* This program is free software; you can redistribute it and/or modify 
* it under the terms of the GNU General Public License as published by 
* the Free Software Foundation; either version 2 of the License, or 
* (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful, but 
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
* for more details.
* 
* You should have received a copy of the GNU General Public License along 
* with this program; if not, write to the Free Software Foundation, Inc., 
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

//Namespace: Global
var Listeners = new Array();

if (typeof (Node) != 'undefined') {
    if (typeof (Node.children) == 'undefined') {
        try {
            eval('Node.prototype.children getter = function() {return this.childNodes;}');
        } catch (ex) { }
    }
}

//Namespace: jObjective
(function() {
    if (Type) Type.registerNamespace("jObjective");
    window.jObjective = {
        _id : 0x0001,
        dispatchEvent: function(eventName, dataObject) {
            var target = null;
            var e;
            var data = dataObject;

            if (document.createEvent) {
                e = document.createEvent('MouseEvents');
                e.initEvent('click', true, true);
            } else {
                e = document.createEventObject();
            }

            e.event_data = data;

            for (var i = 0; i < Listeners.length; i++) {
                var objCurrentListener = Listeners[i];
                if (objCurrentListener.Event == eventName) {
                    target = objCurrentListener.Source;
                    break;
                }
            }

            if (target != null) {
                if (document.createEvent)
                    return target.dispatchEvent(e);
                else
                    return target.fireEvent('onclick', e);
            }
        },
        attachEvent: function(source, funcStr) {
            var funcs = funcStr.split('.');

            var obj = window;

            for (var i = 0; i < funcs.length - 1; i++) {
                obj = obj[funcs[i]];
            }

            if (window.addEventListener)
                source.addEventListener('click', function(e) { obj[funcs[funcs.length - 1]](e.event_data); }, false);
            else
                source.attachEvent('onclick', function(e) { obj[funcs[funcs.length - 1]](e.event_data); });
        },
        addListener: function(eventName, func) {
            for (var i = 0; i < Listeners.length; i++) {
                var objCurrentListener = Listeners[i];
                if (objCurrentListener.Event == eventName) {
                    jObjective.attachEvent(objCurrentListener.Source, func);
                    return;
                }
            }
            var source = document.createElement('a');
            source.style.display = '';

            document.body.appendChild(source);

            jObjective.attachEvent(source, func);

            var objListener = { Event: eventName, Target: func, Source: source };

            Array.add(Listeners, objListener);
        },
        Common: {
            typeOf: function(_T) {
                if (_T == null) return 'undefined';
                var sTypeOf = typeof (_T);

                if (sTypeOf == 'object') {
                    if (typeof (_T.This) != 'undefined') {
                        return 'form';
                    }
                    if (typeof (_T.id) != 'undefined') {
                        return sTypeOf;
                    }
                    if (_T.constructor == (new Array).constructor) {
                        return 'array';
                    }
                    if (typeof (_T.length) != 'undefined') {
                        return 'radiogroup';
                    }
                }
                return sTypeOf;
            },
            round: function(x) { // Because Math.round only takes into consideration the first number after the decimal place.
                x = x * 100;
                var integer = parseInt(x);

                if ((integer + 0.5) > x)
                    return Math.floor(x) / 100;
                else
                    return Math.ceil(x) / 100;
            },
            addOption: function(src, text, value) {
                var option = document.createElement('OPTION');
                option.text = text;
                option.value = value;
                src.options.add(option);
            },
            clearOptions: function(src) {
                for (var i = 0; i < src.options.length; i++) {
                    src.remove(0);
                }
                src.options.length = 0;
            },
            selectOption: function(src, value) {
                src.options[0].selected = true;

                for (var i = 0; i < src.options.length; i++) {
                    if (src.options[i].value == value) {
                        src.options[i].selected = true;
                        return;
                    }
                }
            },
            removeChildren: function(src) {
                var iChildren = src.children.length;
                for (var i = 0; i < iChildren; i++) {
                    src.removeChild(src.children[0]);
                }
            },
            formatFullName: function(personalName) {
                var sName = new String();

                if (personalName.Title != '') {
                    sName += personalName.Title + ' ';
                }
                if (personalName.Forenames != '') {
                    sName += personalName.Forenames + ' ';
                }
                if (personalName.Surname != '') {
                    sName += personalName.Surname + ' ';
                }

                return sName;
            },
            formatLongAddressSummary: function(address) {
                var sAddress = new String();

                if (address.Line1 != '') {
                    sAddress += address.Line1;
                }
                if (address.Line2 != '') {
                    if (sAddress != '') {
                        sAddress += ', '
                    }
                    sAddress += address.Line2;
                }
                if (address.Town != '') {
                    if (sAddress != '') {
                        sAddress += ', '
                    }
                    sAddress += address.Town;
                }
                if (address.County != '') {
                    if (sAddress != '') {
                        sAddress += ', '
                    }
                    sAddress += address.County;
                }
                if (address.Postcode != '') {
                    if (sAddress != '') {
                        sAddress += ', '
                    }
                    sAddress += address.Postcode;
                }

                return sAddress;
            },
            formatShortAddressSummary: function(address) {
                var sAddress = new String();

                if (address.Line1 != '') {
                    sAddress += address.Line1;
                }
                if (address.Line2 != '') {
                    sAddress += ', '
                    sAddress += address.Line2;
                }
                if (address.Town != '') {
                    sAddress += ', '
                    sAddress += address.Town;
                }
                if (address.County != '') {
                    sAddress += ', '
                    sAddress += address.County;
                }

                return sAddress;
            },
            formatContactDetailsSummary: function(contactDetails) {
                var sContactDetails = new String();

                if (contactDetails.Mobile != '') {
                    sContactDetails += 'Mobile: ' + contactDetails.Mobile;
                }
                if (contactDetails.HomeTelephone != '') {
                    if (sContactDetails != '') {
                        sContactDetails += ', '
                    }
                    sContactDetails += 'Home: ' + contactDetails.HomeTelephone;
                }
                if (contactDetails.WorkTelephone != '') {
                    if (sContactDetails != '') {
                        sContactDetails += ', '
                    }
                    sContactDetails += 'Work: ' + contactDetails.WorkTelephone;
                }

                return sContactDetails;
            },
            getDate: function(obj) {
                var objCurrentTime;
                (obj) ? objCurrentTime = obj : objCurrentTime = new Date();
                var sDay = objCurrentTime.getDate() + 1;
                var sMonths = objCurrentTime.getMonth() + 1;
                var sYear = objCurrentTime.getFullYear() + 1;
                var sHours = objCurrentTime.getHours();
                var iMinutes = objCurrentTime.getMinutes();
                var sMinutes;
                if (iMinutes < 10) {
                    sMinutes = "0" + iMinutes.toString();
                } else {
                    sMinutes = iMinutes.toString();
                }
                return sDay + '/' + sMonths + '/' + sYear + ' ' + sHours + ":" + sMinutes;
            },
            error: function(msg) {
                alert(msg.get_message());
            },
            handleError: function(msg) {
                if (msg) {
                    alert('There was a problem processing your request: ' + msg);
                } else {
                    alert('There was a problem processing your request, please try again later.');
                }
            },
            uniqueId: function() {
                jObjective._id++;
                return 'u' + jObjective._id.toString(16);
            }
        }
    }
})();

// Function for creating class instances and inherited classes
// 1 Argument, creates a class from that argument which when instantiated calls the initialize function
// 2 Arguments, creates a class from the 1st argument, and copies the properties of the 2nd argument into it, overridding any existing functions
jObjective.Class = function() {
    var constructor = function() {
        this.initialize.apply(this, arguments);
    }

    var base = arguments[0];

    if (arguments.length == 1) {
        constructor.prototype = base;
    } else {
        var inherited = arguments[1];
        var type = new base;
        
        for (var prop in inherited) {
            var temp = inherited[prop]
            if (temp !== undefined) {
                type[prop] = temp;
            }
        }
        constructor.prototype = type;
    }

    return constructor;
}

/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  DATA OBJECT  **************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.DataObject = jObjective.Class({
    initialize: function(jObject) {
        if (jObject) {
            this._element = jObject[0]; // HTML element reference
            this._jObject = jObject;  //jQuery Selection
        }
    },
    propCache: function() {
        if (!this._propCache) {
            this._propCache = {};

            for (var p in this) {
                var curP = this[p];
                if ((p.charAt(0) != '_') && (typeof (curP) == 'object')) {
                    if (curP instanceof jObjective.DataObject) {
                        this._propCache[p] = curP;
                    }
                }
            }
        }

        return this._propCache;
    },

    // Iterates through the child jObjective.DataObjects and executes the function
    iterate: function(fPtr, value) {
        var arrProps = this.propCache();

        for (var p in arrProps) {
            var curP = arrProps[p];
            curP[fPtr](value);
        }
    },
    // Resets the jObjective.DataObject and its' children
    reset: function() {
        this._element.readOnly = false;
        this.resetStyle();

        this.iterate('reset');
    },
    // Sets the jObjective.DataObject and its' children to Read Only
    readOnly: function() {
        this._element.readOnly = true;
        this.readOnlyStyle();

        this.iterate('readOnly');
    },
    // Iterates through the jObjective.DataObject and its' children creating a simplified JSON object for serialization
    getData: function() {
        var arrProps = this.propCache();
        var objRet = {};

        for (var p in arrProps) {
            var curP = arrProps[p];
            objRet[p] = curP.getData();
        }

        return objRet;
    },
    // Iterates through a simple JSON data object and maps the data to the relevant jObjective.DataObject
    setData: function(value) {
        for (var p in value) {
            if (this[p]) {
                if (typeof (value[p]) != 'undefined')
                    this[p].setData(value[p]);
            }
        }
    },
    // Disables the jObjective.DataObject and its' children
    disable: function() {
        this._element.disabled = true;

        this.iterate('disable');
    },
    // Enables the jObjective.DataObject and its' children
    enable: function() {
        this._element.disabled = false;

        this.iterate('enable');
    },
    hide: function() {
        this._element.style.display = 'none';
    },
    show: function() {
        this._element.style.display = '';
    },
    element: function() {
        return this._element;
    },
    // Simple empty function prototypes
    getValue: function() {
    },
    setValue: function(value) {
    },
    readOnlyStyle: function() {
    },
    resetStyle: function() {
    },
    validate: function() {
        var arrProps = this.propCache();
        var bRetVal = true;

        if (jObjective.Validator) {
            for (var p in arrProps) {
                var curP = arrProps[p];
                bRetVal = (curP.validate() && bRetVal);
            }
        }

        return bRetVal;
    },
    appendChild: function(element) {
        this._propCache = null; // Cache Invalid    
        this._element.appendChild(element);
    },
    removeChildren: function() {
        this._propCache = null;                       // Cache Invalid

        for (var p in this) {
            var curP = this[p];
            if ((p.charAt(0) != '_') && (typeof (curP) == 'object')) {
                if (curP instanceof jObjective.DataObject) {
                    curP.removeChildren();
                    this[p] = null;
                }
            }
        }

        jObjective.Common.removeChildren(this._element);
    },
    beginUpdating: function(hideProcessing) {
        var eParent = this._element.parentNode;

        while (eParent.tagName != 'FORM') {
            eParent = eParent.parentNode;
        }

        if (this._updating) {
            eParent.removeChild(this._updating[0]);
        }

        var objOffset = this._jObject.offset();

        var updating = document.createElement('div');

        updating.id = jObjective.Common.uniqueId();
        updating.className = 'updating';

        eParent.appendChild(updating);

        if (!hideProcessing) {
            var processing = document.createElement('img');
            processing.id = jObjective.Common.uniqueId();
            processing.src = '../style/images/updating.gif';

            updating.appendChild(processing);

            this._processing = $(processing);
            this._processing[0].style.left = (objOffset.left - $(window).scrollLeft() + parseInt((this._jObject.outerWidth() - this._processing.width()) / 2)) + 'px';
            this._processing[0].style.top = (objOffset.top - $(window).scrollTop() + parseInt((this._jObject.outerHeight() - this._processing.height()) / 2)) + 'px';
            this._processing.css('position', 'fixed');
            this._processing.css('z-index', 99999);
        }

        this._updating = $(updating);
        this._updating.css('z-index', 99999);
        this._updating.css('position', 'fixed');
        this._updating[0].style.left = (objOffset.left - 2 - $(window).scrollLeft()) + 'px';
        this._updating[0].style.top = (objOffset.top - 4 - $(window).scrollTop()) + 'px';
        this._updating.width(this._jObject.outerWidth() + 2);
        this._updating.height(this._jObject.outerHeight() + 2);
        this._updating.focus();
    },
    endUpdating: function() {
        if (this._updating) {
            this._updating.css('display', 'none');
        }
        if (this._processing) {
            this._processing.css('display', 'none');
        }
    },
    visible: function() {
        return this._element.style.display == '';
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  DATA ELEMENT  *************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.DataElement = jObjective.Class(jObjective.DataObject, {
    // Terminates the jObjective.DataObject getData recursion by setting the value of the desired element
    getData: function() {
        return this.getValue();
    },
    setData: function(value) {
        return this.setValue(value);
    },
    getValue: function() {
        return this._jObject.val();
    },
    setValue: function(value) {
        this._jObject.val(value);
    },
    focus: function() {
        this._jObject.focus();
    },
    validate: function() {
        return true; //doesn't have any children to validate
    }
});

/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  BUTTON  *******************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.Button = jObjective.Class(jObjective.DataElement, {
    setValue: function(value) {
        if(value.length>0&&value)
            this._element.onclick = function() { eval(value); return false; };
    },
    click: function() {
        this._jObject.click();
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  CHECKBOX  *****************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.CheckBox = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject, readOnly) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection

        this._readOnlyElement = null;
        if (readOnly) { this.readOnly(); }
    },
    readOnlyStyle: function() {
        this._element.style.display = 'none';

        if (!this._readOnlyElement) {
            this._readOnlyElement = document.createElement('label');
            this._readOnlyElement.id = this._element.id + '_readOnly';
            this._readOnlyElement.className = this._element.className + ' ReadOnly';
        }

        (this.getValue()) ? this._readOnlyElement.innerHTML = 'Yes - ' : this._readOnlyElement.innerHTML = 'No - ';

        this._readOnlyElement.style.display = '';
    },
    resetStyle: function() {
        this._element.style.display = '';

        if (this._readOnlyElement) {
            this._readOnlyElement.style.display = 'none';
        }
    },
    getValue: function() {
        return this._element.checked;
    },
    setValue: function(value) {
        this._element.checked = value;
    },
    click: function() {
        this._jObject.click();
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  LABEL  ********************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.Label = jObjective.Class(jObjective.DataElement, {
    getValue: function() {
        return this._element.innerHTML;
    },
    setValue: function(value) {
        this._element.innerHTML = value;
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  LISTBOX  ******************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.Select = jObjective.Class(jObjective.DataElement, {
    initialize: function() {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection

        this._readOnlyElement = null;
    },
    hasSelection: function() {
        return (this._element.selectedIndex != -1);
    },
    selection: function() {
        if (this.hasSelection()) {
            var objItem = this._element.options[this._element.selectedIndex];

            return { Text: objItem.text, Value: objItem.value, Selected: true };
        }
    },
    getText: function() {
        if (this.hasSelection())
            return this._element.options[this._element.selectedIndex].text;
        return '';
    },
    getValue: function() {
        if (this.hasSelection())
            return this._element.options[this._element.selectedIndex].text;
        return '';
    },
    setValue: function(value) {
        var iNum = this._element.options.length;

        for (var i = 0; i < iNum; i++) {
            var objItem = this._element.options[i];
            if (objItem.value == value) {
                objItem.selected = true;
                return;
            }
        }
    },
    getData: function() {
        if (this._cache) {
            //Retrieve the cached options, updating the selection
            if (this.hasSelection())
                this._cache.Options[this._element.selectedIndex].Selected = true;
            return this._cache;
        }

        var objRetVal = {};

        objRetVal.Options = new Array();

        var iNum = this._element.options.length;

        for (var i = 0; i < iNum; i++) {
            var objItem = this._element.options[i];

            Array.add(objRetVal.Options, { Text: objItem.text, Value: objItem.value, Selected: objItem.selected });
        }

        return objRetVal;
    },
    setData: function(item) {
        if (item.Options) {
            this.clearOptions();

            var iNum = item.Options.length;

            for (i = 0; i < iNum; i++) {
                this.addOption(item.Options[i]);
            }

            //Cache the options and deselect the initial selection
            this._cache = item;
            if (this.hasSelection())
                this._cache.Options[this._element.selectedIndex].Selected = false;
        } else {
            this.setValue(item.Value);
        }
    },
    readOnlyStyle: function() {
        this._element.style.display = 'none';

        if (!this._readOnlyElement) {
            this._readOnlyElement = document.createElement('label');
            this._readOnlyElement.id = this._element.id + '_readOnly';
            this._readOnlyElement.className = this._element.className + ' ReadOnly';
        }

        this._readOnlyElement.innerHTML = this.getText();
        this._readOnlyElement.style.display = '';
    },
    resetStyle: function() {
        this._element.style.display = '';

        if (this._readOnlyElement) {
            this._readOnlyElement.style.display = 'none';
        }
    },
    addOption: function(item) {
        var option = document.createElement('OPTION');
        option.text = item.Text;
        option.value = item.Value;
        this._element.options.add(option);

        if (item.Selected) {
            this._element.selectedIndex = this._element.options.length - 1;
        }
    },
    clearOptions: function() {
        for (var i = 0; i < this._element.options.length; i++) {
            this._element.remove(0);
        }
        this._element.options.length = 0;
    },
    setSize: function(value) {
        this._element.size = value;
    }
});
/**************************************************************************************************************************************************/
jObjective.SelectMultiple = jObjective.Class(jObjective.DataElement, {
    initialize: function() {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection

        this._readOnlyElement = null;
    },
    getText: function(seperator) {
        var opts = '';

        var len = this._element.options.length;
        for (var i = 0; i < len; i++) {
            var opt = this._element.options[i];
            if (opt.selected) opts += opt.text + seperator;
        }
        return opts;
    },
    readOnlyStyle: function() {
        this._element.style.display = 'none';

        if (!this._readOnlyElement) {
            this._readOnlyElement = document.createElement('label');
            this._readOnlyElement.id = this._element.id + '_readOnly';
            this._readOnlyElement.className = this._element.className + ' ReadOnly';
        }

        this._readOnlyElement.innerHTML = this.getText('<br />');
        this._readOnlyElement.style.display = '';
    },
    resetStyle: function() {
        this._element.style.display = '';

        if (this._readOnlyElement) {
            this._readOnlyElement.style.display = 'none';
        }
    },
    addOption: function(text, value) {
        var option = document.createElement('OPTION');
        option.text = text;
        option.value = value;
        this._element.options.add(option);
    },
    clearOptions: function() {
        for (var i = 0; i < this._element.options.length; i++) {
            this._element.remove(0);
        }
        this._element.options.length = 0;
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  RADIOBUTTON  **************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.RadioButton = jObjective.Class(jObjective.DataElement, {
    readOnlyStyle: function() {
        this._element.disabled = true;
    },
    resetStyle: function() {
        this._element.disabled = false;
    },
    getValue: function() {
        return this._element.checked;
    },
    setValue: function(value) {
        this._element.checked = value;
    },
    click: function() {
        this._jObject.click();
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  TEXTBOX  ******************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.TextBox = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject, formatter) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection
        this._formatter = formatter;  // Element data formatter

        if (formatter) {
            var objRef = this;
            this._element.onchange = function() { objRef.format(); }
        }

        this._roStyleRE = new RegExp();
        this._roStyleRE.compile('ReadOnly', 'gi');
    },
    readOnlyStyle: function() {
        this._element.className += ' ReadOnly';
    },
    resetStyle: function() {
        this._element.className.replace(this._roStyleRE, '');
        this._element.className = this._element.className.replace(this._roStyleRE, '');
    },
    format: function() {
        this.setValue(this._formatter(this.getValue()));
    },
    setValue: function(value) {
        this._baseSetValue = jObjective.DataElement.prototype.setValue;

        if (this._setFocus)
            this._jObject.focus();

        if (value)
            this._baseSetValue(value);
        else
            this._baseSetValue('');
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*******************************************************************  IVIRTUALPAGE  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.IVirtualPage = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection
        this._current = false;
    },
    setCurrent: function(current) {
        if (typeof (current) != 'undefined') {
            this._current = current;
            (current) ? this.show() : this.hide();
        } else {
            this._current = true;
            this.show();
        }
    },
    isCurrent: function() {
        return this._current;
    },
    setStep: function(nextStep) {
        this._current = false;
        this.hide();

        nextStep.show();
        nextStep.setCurrent();
        return true;
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*******************************************************************  AUTOCOMPLETE  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.AutoComplete = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject, jValue, jList) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection
        this._eValue = jValue[0];
        this._jValue = jValue;
        this._eList = jList[0];
        this._jList = jList;

        this._lastSearch = ''; // Last search text entered
    },
    Validate: function(control) {
        if (jObjective.Validate.NotValidatable(control)) return true;
        var objItem = control.getValue();
        if ((objItem.Text != '') && (objItem.Value != '')) {
            return true;
        }
        return false;
    },
    getData: function() {
        return this.getValue();
    },
    setData: function(value) {
        return this.setValue(value);
    },
    getValue: function() {
        return { Text: this._jObject.val(), Value: this._jValue.val() };
    },
    setValue: function(item) {
        this._jObject.val(item.Text);
        this._jValue.val(item.Value);
    },
    getList: function() {
        if (!this._webMethod) {
            return;
        }

        var objData = { Input: this._element.value, SID: this._SID, Parameter: this._parameter };
        var objRef = this;

        this._webMethod(objData, function(result) { objRef.callback(result); }, jObjective.Common.error, jObjective.Common.error);
        this._lastSearch = this._element.value;
    },
    handleKey: function(keyCode) {
        switch (keyCode) {
            case 40: //Down arrow
                if (this._eList.selectedIndex < this._eList.options.length) {
                    this._eList.selectedIndex++;
                }
                break;
            case 38: //Up arrow
                if (this._eList.selectedIndex > 0) {
                    this._eList.selectedIndex--;
                }
                break;
            case 13: //enter key
                this.setSelection();
                window.event.cancelBubble = true;
                window.event.returnValue = false;
            case 9: //tab key
                this.setSelection();
                break;
            case 27: //escape key
                this._eList.style.display = 'none';
                break;
        }
    },
    checkUpdate: function() {
        if (this._updateTimer)
            clearTimeout(this._updateTimer);

        if (this._element.value.length > 1) {
            if (this._lastSearch != this._element.value) {
                var objRef = this;
                this._updateTimer = setTimeout(function() { objRef.getList(); }, 500);
            } else if (this._jList[0].options.length > 0) {
                this._jList[0].style.display = '';
            }
        }
    },
    setSelection: function() {
        if (this._eList.selectedIndex != -1) {
            var objItem = this._eList.options[this._eList.selectedIndex];

            this._element.value = objItem.text;
            this._eValue.value = objItem.value;
            this._eList.style.display = 'none';

            this._lastSearch = this._element.value;

            var objRef = this;

            if (this._onAutoCompleteSet) {
                eval(this._onAutoCompleteSet + '(objItem);');
            } else {
                jObjective.dispatchEvent('AutoCompleteSet', { Source: this._this, Text: objItem.text, Value: objItem.value });
            }
        }
    },
    hideSelectList: function() {
        this._eList.style.display = 'none';
    },
    removeInputBlur: function() {
        this._element.onblur = null;
    },
    addInputBlur: function() {
        var objRef = this;
        this._element.onblur = function() { objRef.hideSelectList(); };
    },
    callback: function(result) {
        if (result != null) {
            if (result.Success) {
                var iNum = result.DataList.length;

                jObjective.Common.clearOptions(this._eList);

                if (iNum != 0) {
                    for (var i = 0; i < iNum; i++) {
                        var objItem = result.DataList[i];
                        jObjective.Common.addOption(this._eList, objItem.Text, objItem.Value);
                    }
                    var objPos = this._jObject.offset();

                    this._eList.style.left = objPos.left + 'px';
                    this._eList.style.top = objPos.top + this._jObject.outerHeight() + 'px';

                    if (this._eList.options.length == 1) {
                        this._eList.multiple = true;
                    } else {
                        this._eList.multiple = false;
                    }

                    this._eList.size = this._eList.options.length;
                    this._eList.style.height = 'auto';
                    this._eList.style.display = '';
                    this._jList.css('z-index', 99999);
                    this._jList.width(this._jObject.outerWidth());
                }
            } else
                jObjective.Common.handleError(result.ErrorMessage);
        } else {
            jObjective.Common.handleError();
        }
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  HTMLEDITOR  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.HTMLEditor = jObjective.Class(jObjective.DataElement, {
    getValue: function() {
        return this._element.innerHTML;
    },
    setValue: function(value) {
        this._element.innerHTML = value;
    },
    stripHTML: function() {
        var objRef = this;
        setTimeout(function() {
            var regEx = new RegExp('<(?!/[biup])[^biup]{1}.*?>', 'gi'); //Removes all the HTML we don't want

            // Cleans the HTML we do want
            var regExB = /<b(?!r).*?>/gi; //Matches <b*> excluding <br*>
            var regExI = /<i.*?>/gi; //Matches <i*>
            var regExU = /<u.*?>/gi; //Matches <u*>
            var regExP = /<p.*?>/gi; //Matches <p*>

            var sHTML = objRef.getValue()

            sHTML = sHTML.replace(regEx, '');
            sHTML = sHTML.replace(regExB, '<b>');
            sHTML = sHTML.replace(regExI, '<i>');
            sHTML = sHTML.replace(regExU, '<u>');
            sHTML = sHTML.replace(regExP, '<p>');

            objRef.setValue(sHTML);
            objRef.EditorText.setValue(escape(objRef.getValue()));
        }, 100);
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/******************************************************************  NAVIGATIONMAP  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.NavigationMap = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection
        this._stepNumber = new RegExp();
        this._stepNumber.compile(/Page(\d)/);
    },
    transition: function(nextStep, stepNumber) {
        var objCurrentStep = null;
        var iCurrentStep;
        var iNextStep;
        var iMaxStep;

        for (var p in this) {
            var curP = this[p];
            if ((p.charAt(0) != '_') && (typeof (curP) == 'object')) {
                if (curP instanceof jObjective.IVirtualPage) {
                    var iStep = parseInt(p.match(this._stepNumber)[1]);

                    if (curP.isCurrent()) {
                        objCurrentStep = curP;
                        iCurrentStep = iStep;
                    }
                    if (nextStep === curP) {
                        iNextStep = iStep;
                    }
                    iMaxStep = iStep;
                }
            }
        }

        if (objCurrentStep) {
            if (iCurrentStep != iNextStep)
                if (objCurrentStep.setStep(nextStep, (iCurrentStep < iNextStep))) {
                this['Page' + iNextStep + 'Button']._element.className = this._currentButtonCssClass;
                this['Page' + iCurrentStep + 'Button']._element.className = this._buttonCssClass;

                for (var i = 0; i <= iNextStep; i++) {
                    this['Page' + i + 'Button'].enable();
                }
                for (var i = (iNextStep + 1); i <= iMaxStep; i++) {
                    this['Page' + i + 'Button'].disable();
                }
            }
        } else {
            nextStep.init();
            this['Page' + iNextStep + 'Button']._element.className = this._currentButtonCssClass;

            for (var i = 0; i <= iNextStep; i++) {
                this['Page' + i + 'Button'].enable();
            }
            for (var i = (iNextStep + 1); i <= iMaxStep; i++) {
                this['Page' + i + 'Button'].disable();
            }
        }
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/********************************************************************  POPUPDIALOG  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.PopupDialog = {};

jObjective.PopupDialog.Layout = function(popupid) {
    var eForeground = $find(popupid)._foregroundElement;
    var eBackground = $find(popupid)._backgroundElement;
    var eParent = eBackground.parentNode;

    eParent.removeChild(eBackground);
    eParent.removeChild(eForeground);

    while (eParent.tagName != 'FORM') {
        eParent = eParent.parentNode;
    }

    eBackground.style.width = '800px';
    eParent.appendChild(eBackground);
    eParent.appendChild(eForeground);
}
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  WIZARDSTEP  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.WizardStep = jObjective.Class(jObjective.DataElement, {
    initialize: function(jObject) {
        this._element = jObject[0]; // HTML element reference
        this._jObject = jObject;  //jQuery Selection
        this._current = false;
    },
    setStep: function(nextStep, validateStep) {
        this.Options.hide();

        if (validateStep) {
            if (this.validate()) {
                this.beginUpdating();
                this.post(nextStep);
            } else {
                this.Options.show();
                return false;
            }
        } else {
            this.beginUpdating();
            this.post(nextStep);
        }
        return true;
    },
    post: function(nextStep) {
        var objRef = this;
        var objData = this.getData();

        this._postMethod(this._SID, objData, function(result) { objRef.postCallback(result, nextStep); }, jObjective.Common.error, jObjective.Common.error);
    },
    postCallback: function(result, nextStep) {
        if (result != null) {
            if (result.Success) {
                nextStep.init(this);
            } else
                jObjective.Common.handleError(result.ErrorMessage);
        } else {
            jObjective.Common.handleError();
        }
    },
    save: function(hideProgress) {
        this.Options.beginUpdating(true);

        if (!hideProgress)
            this.beginUpdating();

        var objRef = this;
        var objData = this.getData();

        this._saveMethod(this._SID, objData, function(result) { objRef.saveCallback(result, hideProgress); }, jObjective.Common.error, jObjective.Common.error);
    },
    saveCallback: function(result, hideProgress) {
        if (result != null) {
            if (result.Success) {
            } else
                jObjective.Common.handleError(result.ErrorMessage);
        } else {
            jObjective.Common.handleError();
        }
        if (!hideProgress)
            this.endUpdating();

        this.Options.endUpdating();
    },
    init: function(prevStep) {
        var objRef = this;

        this._initMethod(this._SID, function(result) { objRef.initCallback(result, prevStep); }, jObjective.Common.error, jObjective.Common.error);
    },
    initCallback: function(result, prevStep) {
        if (prevStep) {
            prevStep.endUpdating();
            prevStep.Options.show();
            prevStep.setCurrent(false);
        }

        if (result != null) {
            if (result.Success) {
                this.setCurrent();
                this.setData(result.Step);
                this.show();
            } else
                jObjective.Common.handleError(result.ErrorMessage);
        } else {
            jObjective.Common.handleError();
        }

        this.Options.show();
    }
});
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*************************************************************************  FORMAT  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.Format = {
    TelephoneRegExp: new RegExp(),
    CurrencyRegExp: new RegExp(),
    unFormatCurrency: function(num) {
        return parseFloat(num.toString().replace(this.CurrencyRegExp, ''));
    },
    currency: function(num, showpence) {
        num = this.UnFormatCurrency(num);
        if (isNaN(num))
            num = '0';
        sign = (num == (num = Math.abs(num)));
        num = Math.floor(num * 100 + 0.50000000001);
        pence = num % 100;
        num = Math.floor(num / 100).toString();
        if (pence < 10)
            pence = '0' + pence;
        for (var i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++)
            num = num.substring(0, num.length - (4 * i + 3)) + ',' +
        num.substring(num.length - (4 * i + 3));

        if (showpence == true)
            return (((sign) ? '' : '-') + '£' + num + '.' + pence);
        else
            return (((sign) ? '' : '-') + '£' + num);
    },
    properNoun: function(value) {
        if (value != '') {
            return value.charAt(0).toUpperCase() + value.substr(1);
        } else {
            return '';
        }
    },
    postcode: function(str) {
        str = str.toUpperCase().replace(' ', '');
        if (str.length >= 5) {
            str = str.substr(0, str.length - 3) + ' ' + str.substr(str.length - 3, 3);
        }
        return str;
    },
    contactNumber: function(value) {
        return value.replace(this.TelephoneRegExp, ' ');
    }
}

jObjective.Format.TelephoneRegExp.compile('[^0-9\+]+', 'gi');
jObjective.Format.CurrencyRegExp.compile('\£|\,', 'g');
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  VALIDATION  ***************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/****************************** GROUP OPERATORS ********************************************/
jObjective.andOperator = function(lhs, rhs) {
    return lhs && rhs;
}
jObjective.orOperator = function(lhs, rhs) {
    return lhs || rhs;
}
/*******************************************************************************************/

jObjective.Validator = jObjective.Class(jObjective.DataObject, {
    initialize: function(valElement, valFunc, valMsg, operator) {
        this._valElement = valElement; //Element to validate
        this._valFunc = valFunc;  // Validation function
        this._valMsg = valMsg;    // Validation element reference
        this._operator = operator; // Result combination operator
    },
    // Validates the jObjective.Validator and its' children
    validate: function() {
        var arrProps = this.propCache();

        var bRetVal = (this._operator === jObjective.andOperator) ? true : false;

        if ((this._valElement != null) && (this._valFunc != null)) {
            if (!(this._valElement instanceof jObjective.Validator)) {
                bRetVal = this._valFunc(this._valElement);
                (bRetVal) ? this._valMsg.css('display', 'none') : this._valMsg.css('display', '');
            } else {
                bRetVal = this._operator(this._valElement.validate(), bRetVal);
            }
        }

        for (var p in arrProps) {
            var curP = arrProps[p];

            if (curP instanceof jObjective.Validator) {
                bRetVal = this._operator(curP.validate(), bRetVal);
            }
        }

        // Children will have their failed messages displaying, but due to the OR operation validation has actually passed, so reset the children.
        if ((this._operator === jObjective.orOperator) && (bRetVal)) {
            this.reset();
        }

        (bRetVal) ? this._valMsg.css('display', 'none') : this._valMsg.css('display', '');

        return bRetVal;
    },
    // Resets the jObjective.Validator and its' children
    reset: function() {
        var arrProps = this.propCache();

        if (this._valMsg != null) this._valMsg.css('display', 'none');

        for (var p in arrProps) {
            var curP = arrProps[p];
            if (curP instanceof jObjective.Validator) {
                curP.reset();
            }
        }
    }
});
/*******************************************************************************************/
jObjective.Validate = {
    ContactRegExp: new RegExp(),
    EmailRegExp: new RegExp(),
    NumberRegExp: new RegExp(),
    PostCodeRegExp: new RegExp(),
    DateRegExp: new RegExp(),
    YearRegExp: new RegExp(),
    MonthRegExp: new RegExp(),
    TimeRegExp: new RegExp()
}

jObjective.Validate.ContactRegExp.compile('.\\d+.*\\d+.*\\d+.*\\d+.*\\d+.*\\d+.*\\d+.*');
jObjective.Validate.EmailRegExp.compile('^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$');
jObjective.Validate.NumberRegExp.compile('\\d+');
jObjective.Validate.PostCodeRegExp.compile('^([A-Z][A-Z]?[0-9R][0-9A-Z]? *[0-9][A-Z][A-Z])$');
jObjective.Validate.DateRegExp.compile('(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](1[0-9][0-9][0-9]|2[0][0-9][0-9])');
jObjective.Validate.YearRegExp.compile('[0-9]?[0-9]');
jObjective.Validate.MonthRegExp.compile('0[1-9]|1[0-2]');
jObjective.Validate.TimeRegExp.compile('[0-2]?[0-9][:][0-5][0-9]');

/****************************** OPTIONAL VALIDATION ********************************************/
jObjective.Validate.contactNumber = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.ContactRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.email = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.EmailRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.currency = function(control) {
    var value = jObjective.Format.unFormatCurrency(control.getValue());

    if (value > 0) {
        if (jObjective.Format.unFormatCurrency(value).toString().match(jObjective.Validate.NumberRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.number = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.NumberRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.postcode = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.PostCodeRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.date = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.DateRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}

jObjective.Validate.time = function(control) {
    if (control.getValue().length > 0) {
        if (control.getValue().match(jObjective.Validate.TimeRegExp) != null)
            return true;
        else
            return false;
    } else return true;
}
/****************************** REQUIRED VALIDATION ********************************************/
jObjective.Validate.requiredReference = function(control) {
    if (jObjective.Validate.notValidatable(control.parentNode.parentNode)) return true;
    if (control.getValue().length > 0) {
        return true;
    } else {
        return false;
    }
}

jObjective.Validate.requiredString = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().length > 0) {
        return true;
    } else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredHTML = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().length > 0) {
        return true;
    } else {
        control.focus();
        return false;
    }
}
jObjective.Validate.requiredContactNumber = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().match(jObjective.Validate.ContactRegExp) != null)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredContactNumbers = function(obj) {
    if (jObjective.Validate.notValidatable(obj.Mobile)) return true;

    var iHome = obj.Home.value;
    var iWork = obj.Work.value;
    var iMobile = obj.Mobile.value;

    if ((iHome.match(this.ContactRegExp) != null) ||
        (iWork.match(this.ContactRegExp) != null) ||
        (iMobile.match(this.ContactRegExp) != null))
        return true;
    else {
        return false;
    }
}

jObjective.Validate.requiredEmail = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().match(jObjective.Validate.EmailRegExp) != null)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredCurrency = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    var rMatch = jObjective.Format.unFormatCurrency(control.getValue()).toString().match(jObjective.Validate.NumberRegExp);
    if (rMatch != null && rMatch > 0)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredNumber = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    var rMatch = control.getValue().match(jObjective.Validate.NumberRegExp);
    if (rMatch != null && rMatch > 0)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.RequiredPostcode = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().match(jObjective.Validate.PostCodeRegExp) != null)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredDate = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().match(jObjective.Validate.DateRegExp) != null)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredTime = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue().match(jObjective.Validate.TimeRegExp) != null)
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredDropDown = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue() == '-- Please Specify --' || control.getValue() == -1) {
        control.focus();
        return false;
    }
    else {
        return true;
    }
}
jObjective.Validate.requiredCheckbox = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    if (control.getValue())
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.requiredCardDate = function(obj) {
    if (jObjective.Validate.notValidatable(obj.Year) || jObjective.Validate.notValidatable(obj.Month)) return true; //visibility check
    if ((obj.Year.getValue().toString().match(jObjective.Validate.YearRegExp) != null) &&
        (obj.Month.getValue().toString().match(jObjective.Validate.MonthRegExp) != null))
        return true;
}

jObjective.Validate.requiredExpiryDate = function(obj) {
    if (jObjective.Validate.notValidatable(obj.Year) || jObjective.Validate.notValidatable(obj.Month)) return true; //visibility check
    var iYear = obj.Year.getValue();
    var iMonth = obj.Month.getValue();

    if (iYear.length == 1) {
        iYear = 200 + iYear;
    } else {
        iYear = 20 + iYear;
    }

    var dtToday = new Date();
    var dtExpiry = new Date(iYear, iMonth);

    if (dtToday.getTime() > dtExpiry.getTime()) {
        obj.Year.focus();
        return false;
    } else
        return true;
}

jObjective.Validate.requiredAddressRef = function(control) {
    if (control.getValue().length > 0) {
        return true;
    } else {
        return false;
    }
}

jObjective.Validate.requiredCV2 = function(control) {
    if (jObjective.Validate.notValidatable(control)) return true;
    var rMatch = control.getValue().match(jObjective.Validate.NumberRegExp);
    if ((rMatch != null) && (rMatch.toString().length == 3))
        return true;
    else {
        control.focus();
        return false;
    }
}

jObjective.Validate.hiddenField = function(control) {
    if (control.element().parentNode.offsetHeight == 0) return true;
    if ((control.getValue().length > 0) && (control.getValue() != '-1'))
        return true;
    else
        return false;
}
/*******************************************************************************************/
jObjective.Validate.notValidatable = function(control) {
    return (control.element().offsetHeight == 0) || (control.element().disabled); //not visible or disabled
}
/*******************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
/*********************************************************************  PopupBox  *****************************************************************/
/**************************************************************************************************************************************************/
/**************************************************************************************************************************************************/
jObjective.PopupBox = jObjective.Class(jObjective.DataObject, {
    show: function(offset) {
        this._baseShow = jObjective.DataObject.prototype.show;

        if (this._element.style.display = '') {
            this.hide();
        } else {
            this._element.style.left = offset.left + 'px';
            this._element.style.top = offset.top + 'px';

            this._baseShow();
        }
    }
});